/*----------------------------------------------------------
add head  and crc to the input data(axi-stream)
use fifo to cache the current input data,orgnaiz the head part and read the input data
from fifo
s_axis_tuser define as below:
[32]---broadcast_flag
[31:16]---type
[15:0]---len
------------------------------------------------------------*/
module mac_tx#(
    parameter                   TARGET_MAC = 48'hff_ff_ff_ff_ff_ff      ,
    parameter                   SOURCE_MAC = 48'h11_22_33_44_55_66           
)(
    input                       i_clk                                   ,
    input                       i_rst                                   ,
    input   [47: 0]             i_set_source_mac                        ,
    input                       i_set_source_mac_valid                  ,
    input   [47: 0]             i_arp_info_mac0                         ,
    input   [31: 0]             i_arp_info_ip0                          ,
    input   [47: 0]             i_arp_info_mac1                         ,
    input   [31: 0]             i_arp_info_ip1                          ,
    input                       s_axis_tvalid                           ,
    input   [7 : 0]             s_axis_tdata                            ,
    output                      s_axis_tready                           ,
    input                       s_axis_tlast                            ,
    input   [64: 0]             s_axis_tuser                            ,
    output  [7 : 0]             o_gmii_txdata                           ,
    output                      o_gmii_txvalid                          
);

    localparam                  MAC_DILE_CNT = 12                       ;

    logic   [47: 0]             arp_info_mac0                           ;
    logic   [31: 0]             arp_info_ip0                            ;
    logic   [47: 0]             arp_info_mac1                           ;
    logic   [31: 0]             arp_info_ip1                            ;
    logic   [47: 0]             mac_tx_target_mac                       ;
    logic   [31: 0]             mac_tx_target_ip                        ;
    logic   [47: 0]             set_source_mac                          ;
    logic   [7 : 0]             axis_tx_tdata                           ;
    logic                       axis_tx_tlast                           ;
    logic                       axis_tx_tvalid                          ;
    logic                       axis_tx_tready                          ;
    logic                       tx_handshake                            ;
    logic                       mac_broadcast_flag                      ;
    logic   [15: 0]             mac_pkg_len                             ;
    logic   [15: 0]             mac_type                                ;
    logic                       fifo_rden                               ;
    logic                       fifo_rden_delay                         ;
    logic   [7 : 0]             fifo_rddata                             ;
    logic                       fifo_full                               ;
    logic                       fifo_empty                              ;
    logic   [15: 0]             lsm_cnt                                 ;
    logic                       mac_tx_data_valid                       ;
    logic                       mac_tx_data_valid_d                     ;
    logic   [7 : 0]             mac_tx_data                             ;
    logic                       mac_tx_crc_valid                        ;
    logic   [31: 0]             mac_tx_crc_result                       ;
    logic                       mac_crc_en                              ;
    logic                       crc_rst                                 ;
    logic   [2 : 0]             crc_cnt                                 ;
    logic   [7 : 0]             gmii_txdata                             ;
    logic                       gmii_txvalid                            ;
    logic   [7 : 0]             mac_idle_cnt                            ;

    

    

    assign s_axis_tready = axis_tx_tready;
    assign o_gmii_txdata = gmii_txdata;
    assign o_gmii_txvalid = gmii_txvalid;

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            set_source_mac <= SOURCE_MAC;
        else if(i_set_source_mac_valid)
            set_source_mac <= i_set_source_mac;
        else 
            set_source_mac <= set_source_mac;
    end
  
    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            arp_info_mac0 <= 0;
            arp_info_ip0 <= 0;
            arp_info_mac1 <= 0;
            arp_info_ip1 <= 0;
        end
        else begin
            arp_info_mac0 <= i_arp_info_mac0;
            arp_info_ip0 <= i_arp_info_ip0 ;
            arp_info_mac1 <= i_arp_info_mac1;
            arp_info_ip1 <= i_arp_info_ip1 ;
        end
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_tdata <= 0;
        else 
            axis_tx_tdata <= s_axis_tdata;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_tvalid <= 0;
        else
            axis_tx_tvalid <= s_axis_tvalid;
    end


    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_tlast <= 0;
        else
            axis_tx_tlast <= s_axis_tlast;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            tx_handshake <= 0;
        else 
            tx_handshake <= s_axis_tvalid & s_axis_tready;
    end
    

    //cache the input data
    sfifo_8x2048 sfifo_8X2048_inst0 (
        .clk                        ( i_clk                         ),
        .srst                       ( i_rst                         ),
        .din                        ( axis_tx_tdata                 ),
        .wr_en                      ( tx_handshake                  ),
        .rd_en                      ( fifo_rden                     ),
        .dout                       ( fifo_rddata                   ),
        .full                       ( fifo_full                     ),
        .empty                      ( fifo_empty                    ) 
    );

    CRC32_D8 CRC32_D8_inst(
        .data_in                    ( mac_tx_data                   ),
        .crc_en                     ( mac_crc_en                    ),
        .crc_out                    ( mac_tx_crc_result             ),
        .rst                        ( i_rst|crc_rst                 ),
        .clk                        ( i_clk                         )
    );

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            mac_broadcast_flag <= 0;
            mac_pkg_len <= 0;
            mac_type <= 0; 
            mac_tx_target_ip <= 0;
        end
        else if(s_axis_tvalid & s_axis_tready) begin
            mac_broadcast_flag <= s_axis_tuser[64];
            mac_type <= s_axis_tuser[31:16];
            if(s_axis_tuser[15:0]<46) begin
                mac_pkg_len <= 46 + 24;
            end
            else  begin
                mac_pkg_len <= s_axis_tuser[15:0] + 22;
            end

            mac_tx_target_ip <= s_axis_tuser[63:32];

        end
        else begin
            mac_broadcast_flag <= mac_broadcast_flag;
            mac_type <= mac_type;
            mac_tx_target_ip <= mac_tx_target_ip;
        end  
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            mac_tx_target_mac <= SOURCE_MAC;
        end
        else if(s_axis_tvalid && s_axis_tready && mac_tx_target_ip == arp_info_ip0) begin
            mac_tx_target_mac <= arp_info_mac0;
        end
        else if(s_axis_tvalid && s_axis_tready && mac_tx_target_ip == arp_info_ip1) begin
            mac_tx_target_mac <= arp_info_mac1;
        end
        else if(s_axis_tvalid && s_axis_tready) begin
            mac_tx_target_mac <= 48'hff_ff_ff_ff_ff_ff;
        end
        else begin
            mac_tx_target_mac <= mac_tx_target_mac;
        end
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_tready <= 1;
        else if(s_axis_tlast)
            axis_tx_tready <= 0;
        else if(axis_tx_tready == 0 && mac_idle_cnt == MAC_DILE_CNT-3)
            axis_tx_tready <= 1;
        else  
            axis_tx_tready <= axis_tx_tready;
    end  
    


    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            lsm_cnt <= 0;
        else if(lsm_cnt == mac_pkg_len)
            lsm_cnt <= 0;
        else if(!fifo_empty || lsm_cnt)
            lsm_cnt <= lsm_cnt + 1;
        else 
            lsm_cnt <= lsm_cnt;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_tx_data_valid <= 0;
        else if(lsm_cnt == mac_pkg_len)
            mac_tx_data_valid <= 0;
        else if(!fifo_empty)
            mac_tx_data_valid <= 1;
        else 
            mac_tx_data_valid <= mac_tx_data_valid;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_tx_data_valid_d <= 0;
        else 
            mac_tx_data_valid_d <= mac_tx_data_valid;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            fifo_rden <= 0;
        else if(lsm_cnt >= 21 && lsm_cnt < mac_pkg_len-1)
            fifo_rden <= 1;
        else  
            fifo_rden <= 0;
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            fifo_rden_delay <= 0;
        else  
            fifo_rden_delay <= fifo_rden;
    end 
    
    //genarate the 22bytes of mac header 
    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_tx_data <= 0;
        else 
            case (lsm_cnt)
                0 : mac_tx_data <= 8'h55; 
                1 : mac_tx_data <= 8'h55; 
                2 : mac_tx_data <= 8'h55; 
                3 : mac_tx_data <= 8'h55; 
                4 : mac_tx_data <= 8'h55; 
                5 : mac_tx_data <= 8'h55; 
                6 : mac_tx_data <= 8'h55; 
                7 : mac_tx_data <= 8'hd5;
                8 : mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[47:40];//dst_mac
                9 : mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[39:32];
                10: mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[31:24];
                11: mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[23:16];
                12: mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[15: 8];
                13: mac_tx_data <= (mac_broadcast_flag == 1)? 8'hff : mac_tx_target_mac[7 : 0];
                14: mac_tx_data <= set_source_mac[47:40];//src_mac
                15: mac_tx_data <= set_source_mac[39:32];
                16: mac_tx_data <= set_source_mac[31:24];
                17: mac_tx_data <= set_source_mac[23:16];
                18: mac_tx_data <= set_source_mac[15: 8];
                19: mac_tx_data <= set_source_mac[7 : 0];
                20: mac_tx_data <= mac_type[15:8];//type
                21: mac_tx_data <= mac_type[7:0];
                default :mac_tx_data <= (fifo_empty == 1)? 8'h00 : fifo_rddata;
            endcase
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            crc_rst <= 0;
        else if(crc_cnt == 3)
            crc_rst <= 1;
        else 
            crc_rst <= 0;
    end 
    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_crc_en <= 0;
        else if(lsm_cnt >= 8 && lsm_cnt <= mac_pkg_len-1)
            mac_crc_en <= 1;
        else 
            mac_crc_en <= 0;
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            crc_cnt <= 0;
        else if(crc_cnt==3)
            crc_cnt <= 0;
        else if(( mac_tx_data_valid==0 && mac_tx_data_valid_d==1) | crc_cnt)
            crc_cnt <= crc_cnt + 1;
        else 
            crc_cnt <= crc_cnt;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_tx_crc_valid <= 0;
        else if(crc_cnt == 3)
            mac_tx_crc_valid <= 0;
        else if(!fifo_rden & fifo_rden_delay)
            mac_tx_crc_valid <= 1;
        else 
            mac_tx_crc_valid <= mac_tx_crc_valid;
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            gmii_txdata <= 0;
        end
        else if(mac_tx_data_valid)
            gmii_txdata <= mac_tx_data;
        else if(mac_tx_crc_valid)
            case (crc_cnt)
                0 : gmii_txdata <= mac_tx_crc_result[7 : 0]; 
                1 : gmii_txdata <= mac_tx_crc_result[15: 8]; 
                2 : gmii_txdata <= mac_tx_crc_result[23:16]; 
                3 : gmii_txdata <= mac_tx_crc_result[31:24]; 
                default :gmii_txdata <= 0;
            endcase
        else 
            gmii_txdata <= 0;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            gmii_txvalid <= 0;
        else 
            gmii_txvalid <= mac_tx_crc_valid|mac_tx_data_valid;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            mac_idle_cnt <= 0;
        else if(mac_idle_cnt >= MAC_DILE_CNT)
            mac_idle_cnt <= 0;
        else if(crc_rst | mac_idle_cnt)
            mac_idle_cnt <= mac_idle_cnt + 1;
        else 
            mac_idle_cnt <= mac_idle_cnt;
    end  
   

endmodule
